package com.ajaxjs.data_service.mybatis;

import java.text.DateFormat;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.Properties;
import java.util.regex.Matcher;

import com.ajaxjs.util.logger.LogHelper;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.type.TypeHandlerRegistry;

/**
 * 打印完整 SQL https://www.iteye.com/blog/zhanghteye-2373231
 * 
 * @author https://blog.csdn.net/y368769/article/details/110479957
 *
 */
@Intercepts({ @Signature(type = Executor.class, method = "query", args = { MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class }),
		@Signature(type = Executor.class, method = "update", args = { MappedStatement.class, Object.class }) })
public class MybatisInterceptor implements Interceptor {
	private static final LogHelper LOGGER = LogHelper.getLog(MybatisInterceptor.class);

	@SuppressWarnings("unused")
	private Properties properties;

	@Override
	public Object intercept(Invocation invocation) throws Throwable {
		try {
			MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];
			Object parameter = null;

			if (invocation.getArgs().length > 1)
				parameter = invocation.getArgs()[1];

			String sqlId = mappedStatement.getId();
			BoundSql boundSql = mappedStatement.getBoundSql(parameter);
			Configuration configuration = mappedStatement.getConfiguration();

			String sql = getSql(configuration, boundSql, sqlId, 0);
			LOGGER.info("实际执行 SQL：" + sql);
		} catch (Exception e) {
			LOGGER.warning(e);
		}

		return invocation.proceed();
	}

	/**
	 * 
	 * @param cfg
	 * @param sql
	 * @param sqlId
	 * @param time
	 * @return
	 */
	public static String getSql(Configuration cfg, BoundSql sql, String sqlId, long time) {
		String _sql = showSql(cfg, sql);
		StringBuilder sb = new StringBuilder(100);
		sb.append(sqlId);
		sb.append(" : ");
		sb.append(_sql);

		return sb.toString();
	}

	/**
	 * 
	 * 
	 * @param obj
	 * @return
	 */
	public static String getParameterValue(Object obj) {
		String value = null;

		if (obj instanceof String)
			value = "'" + obj.toString() + "'";
		else if (obj instanceof Date) {
			DateFormat formatter = DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT, Locale.CHINA);
			value = "'" + formatter.format(new Date()) + "'";
		} else
			value = obj != null ? obj.toString() : "";

		return value;
	}
	
	/**
	 *  @author:Shuoshi.Yan
	 * @param cfg
	 * @param boundSql
	 * @return
	 */
	public static String showSql(Configuration cfg, BoundSql boundSql) {
		Object parameterObject = boundSql.getParameterObject();
		List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
		String sql = boundSql.getSql().replaceAll("[\\s]+", " ");

		if (parameterMappings != null && parameterMappings.size() > 0 && parameterObject != null) {
			TypeHandlerRegistry typeHandlerRegistry = cfg.getTypeHandlerRegistry();

			if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass()))
				sql = sql.replaceFirst("\\?", Matcher.quoteReplacement(getParameterValue(parameterObject)));
			else {
				MetaObject metaObject = cfg.newMetaObject(parameterObject);
				
				for (ParameterMapping parameterMapping : parameterMappings) {
					String propertyName = parameterMapping.getProperty();

					if (metaObject.hasGetter(propertyName)) {
						Object obj = metaObject.getValue(propertyName);
						sql = sql.replaceFirst("\\?", Matcher.quoteReplacement(getParameterValue(obj)));
					} else if (boundSql.hasAdditionalParameter(propertyName)) {
						Object obj = boundSql.getAdditionalParameter(propertyName);
						sql = sql.replaceFirst("\\?", Matcher.quoteReplacement(getParameterValue(obj)));
					} else {
						sql = sql.replaceFirst("\\?", "缺失");
					} // 打印出缺失，提醒该参数缺失并防止错位
				}
			}
		}

		return sql;
	}

	@Override
	public Object plugin(Object target) {
		return Plugin.wrap(target, this);
	}

	@Override
	public void setProperties(Properties properties0) {
		this.properties = properties0;
	}
}